/*******************************************************************/
/* @ All Rights Reserved Copyright (C) 2014 broada,Co.,Ltd.        */
/*                                                                 */
/*  NAME      = frstat_thread.c                                    */
/*  NOTE      =                                                    */
/*  DATE      = 2015/01/23 by guojq                                */
/*******************************************************************/
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include <pthread.h>

#include "frcomm.h"
#include "frlog.h"
#include "frmem.h"
#include "frhash2.h"

#define THREAD_STAT_HASH_SIZE   200

typedef struct __thread_info {
    unsigned long ul_task_dones;
    unsigned long ul_errors;
    unsigned long ul_requests;
    unsigned long ul_responses;
} Frstat_thread_info;

typedef struct __thread_info_avg {
    unsigned long ul_task_dones[4];
    unsigned long ul_errors[4];
    unsigned long ul_requests[4];
    unsigned long ul_responses[4];
} Frstat_thread_avg;

typedef struct __thread_stat {
    struct timeval start_time; //线程开始时间
    struct timeval run_time; //线程开始时间
    
    unsigned long int i_thread_id;
    Frstat_thread_info st_thread_total;
    Frstat_thread_avg  st_thread_avg;
} Frstat_thread;

static Frhash_header g_st_hash_thread;
static pthread_rwlock_t g_stat_thread_rwlock;
static int g_i_timeout = 1;
static int g_i_threads_total = 0;
static int g_i_flag_init = FR_FAIL;

/*******************************************************************************
*  FUNC     :  释放节点内容
*  ARGS     :  p_node 
*  RTN      :
*  NOTE     :  
*******************************************************************************/
static void __thread_node_free(Frlist_node* p_node)
{
    Frstat_thread *p_stat_node = NULL;
    
    if (p_node && p_node->data)
    {
        p_stat_node = (Frstat_thread *)p_node->data;
        frfree(p_stat_node);
    }
}

/*******************************************************************************
*  FUNC     :  计算平均值
*  ARGS     :  arr_ul_last          总数表
*           :  p_avg                算好的平均值
*  RTN      :
*  NOTE     :
*******************************************************************************/
static void __count_avg(unsigned long arr_ul_last[4], unsigned long *p_avg)
{
    if (arr_ul_last[0] == 0 || arr_ul_last[1] == 0)
    {
        //记录的是总数，因此前2个节点为0的情况说明平均为0或者是刚开始统计，平均设成0
        *p_avg = 0;
        return;
    }
    if (arr_ul_last[2] == 0)
    {
        *p_avg = (arr_ul_last[0] - arr_ul_last[1])/g_i_timeout;
        return;
    }
    if (arr_ul_last[3] == 0)
    {
        *p_avg = (arr_ul_last[0] - arr_ul_last[2])/(g_i_timeout*2);
        return;
    }
    *p_avg = (arr_ul_last[0] - arr_ul_last[3])/(g_i_timeout*3);
    return;
}

/*******************************************************************************
*  FUNC     :  计算节点的平均信息
*  ARGS     :  p_node               线程节点
*           :  p_data               节点的信息
*  RTN      :  1 继续做下一个节点
*  NOTE     :  
*******************************************************************************/
static int __frstat_thread_count(Frlist_node* p_node, const void *p_data)
{
    Frstat_thread *p_stat_node = NULL;
    
    p_stat_node = (Frstat_thread *)p_node->data;
    
    memmove(p_stat_node->st_thread_avg.ul_task_dones + 1, p_stat_node->st_thread_avg.ul_task_dones, sizeof(unsigned long)*3);
    memmove(p_stat_node->st_thread_avg.ul_errors + 1, p_stat_node->st_thread_avg.ul_errors, sizeof(unsigned long)*3);
    memmove(p_stat_node->st_thread_avg.ul_requests + 1, p_stat_node->st_thread_avg.ul_requests, sizeof(unsigned long)*3);
    memmove(p_stat_node->st_thread_avg.ul_responses + 1, p_stat_node->st_thread_avg.ul_responses, sizeof(unsigned long)*3);
    
    p_stat_node->st_thread_avg.ul_task_dones[0] = p_stat_node->st_thread_total.ul_task_dones;
    p_stat_node->st_thread_avg.ul_errors[0] = p_stat_node->st_thread_total.ul_errors;
    p_stat_node->st_thread_avg.ul_requests[0] = p_stat_node->st_thread_total.ul_requests;
    p_stat_node->st_thread_avg.ul_responses[0] = p_stat_node->st_thread_total.ul_responses;
    
    return 1;
}

/*******************************************************************************
*  FUNC     :  计算所有线程节点的平均信息
*  ARGS     :  
*  RTN      :  
*  NOTE     :  
*******************************************************************************/
void frstat_thread_count(void)
{
    if(g_i_flag_init == FR_FAIL)
        return;

    pthread_rwlock_wrlock(&g_stat_thread_rwlock);
    frhash2_run(&g_st_hash_thread, NULL, __frstat_thread_count);
    pthread_rwlock_unlock(&g_stat_thread_rwlock);
}

/*******************************************************************************
*  FUNC     :  添加线程信息
*  ARGS     :  i_thread_id          线程id
*           :  i_task_done          线程增加的任务数
*           :  i_error              线程增加的错误数
*           :  i_request            线程增加的request数
*           :  i_response           线程增加的response数
*  RTN      :  FR_SUCC      (成功)
*           :  FR_FAIL      (失败)
*  NOTE     :  
*******************************************************************************/
int frstat_thread_add(unsigned long int i_thread_id, int i_task_done, int i_error, int i_request, int i_response)
{
    Frlist_node *p_st_node = NULL;
    Frstat_thread *p_new_thread = NULL;

    if(g_i_flag_init == FR_FAIL)
        return FR_FAIL;
    
    pthread_rwlock_wrlock(&g_stat_thread_rwlock);
    p_st_node = frhash2_getLong(&g_st_hash_thread, i_thread_id);
    if (p_st_node && p_st_node->data)
    {
        Frstat_thread *p_stat_thread = (Frstat_thread *)p_st_node->data;
        
        p_stat_thread->st_thread_total.ul_task_dones += i_task_done;
        p_stat_thread->st_thread_total.ul_errors += i_error;
        p_stat_thread->st_thread_total.ul_requests += i_request;
        p_stat_thread->st_thread_total.ul_responses += i_response;
        gettimeofday(&p_stat_thread->run_time, 0);
        pthread_rwlock_unlock(&g_stat_thread_rwlock);
        
        return FR_SUCC;
    }
    p_new_thread = frmalloc(sizeof(Frstat_thread));
    if (p_new_thread == NULL)
    {
        pthread_rwlock_unlock(&g_stat_thread_rwlock);
        return FR_FAIL;
    }
    memset(p_new_thread, 0x00, sizeof(Frstat_thread));
    
    p_new_thread->i_thread_id = i_thread_id;
    p_new_thread->st_thread_total.ul_task_dones = i_task_done;
    p_new_thread->st_thread_total.ul_errors = i_error;
    p_new_thread->st_thread_total.ul_requests = i_request;
    p_new_thread->st_thread_total.ul_responses = i_response;
    gettimeofday(&p_new_thread->start_time, 0);
    gettimeofday(&p_new_thread->run_time, 0);
    
    p_st_node = frlist_node_create(p_new_thread);
    if (p_st_node == NULL)
    {
        frfree(p_new_thread);
        pthread_rwlock_unlock(&g_stat_thread_rwlock);
        return FR_FAIL;
    }
    frhash2_addLong(&g_st_hash_thread, i_thread_id, p_st_node);
    g_i_threads_total++;
    
    pthread_rwlock_unlock(&g_stat_thread_rwlock);
    return FR_SUCC;
}

/*******************************************************************************
*  FUNC     :  删除某线程id的信息
*  ARGS     :  i_thread_id          线程id
*  RTN      :  FR_SUCC (成功:预留)
*  NOTE     :  
*******************************************************************************/
int frstat_thread_del(unsigned long int i_thread_id)
{
    if(g_i_flag_init == FR_FAIL)
        return FR_FAIL;

    pthread_rwlock_wrlock(&g_stat_thread_rwlock);
    frhash2_delLong(&g_st_hash_thread, i_thread_id, __thread_node_free);
    g_i_threads_total--;
    pthread_rwlock_unlock(&g_stat_thread_rwlock);
    
    return FR_SUCC;
}

/*******************************************************************************
*  FUNC     :  显示节点的线程信息
*  ARGS     :  p_node               线程节点
*           :  p_outbuff            出力内容
*           :  ui_outlen            出力长度
*  RTN      :  
*  NOTE     :  
*******************************************************************************/
void __frstat_thread_output(Frlist_node* p_node, char *p_outbuff, unsigned int ui_outlen)
{
    Frstat_thread *p_stat_node = NULL;
    unsigned long ul_task_dones_avg = 0;
    unsigned long ul_errors_avg = 0;
    unsigned long ul_requests_avg = 0;
    unsigned long ul_responses_avg = 0;
    struct tm *p_tm = NULL;
    struct tm *p_tm_run = NULL;
    char arr_start[50], arr_end[50];
    
    p_stat_node = (Frstat_thread *)p_node->data;
    
    __count_avg(p_stat_node->st_thread_avg.ul_task_dones, &ul_task_dones_avg);
    __count_avg(p_stat_node->st_thread_avg.ul_errors, &ul_errors_avg);
    __count_avg(p_stat_node->st_thread_avg.ul_requests, &ul_requests_avg);
    __count_avg(p_stat_node->st_thread_avg.ul_responses, &ul_responses_avg);
    
    p_tm = localtime(&p_stat_node->start_time.tv_sec);
    snprintf(arr_start, sizeof(arr_start), "%04d/%02d/%02d %02d:%02d:%02d"
            , p_tm->tm_year + 1900,p_tm->tm_mon + 1,p_tm->tm_mday
            , p_tm->tm_hour,p_tm->tm_min,p_tm->tm_sec);
    p_tm_run = localtime(&p_stat_node->run_time.tv_sec);
    snprintf(arr_end, sizeof(arr_end), "%04d/%02d/%02d %02d:%02d:%02d"
            , p_tm_run->tm_year + 1900,p_tm_run->tm_mon + 1,p_tm_run->tm_mday
            , p_tm_run->tm_hour,p_tm_run->tm_min,p_tm_run->tm_sec);
    
    snprintf(p_outbuff, ui_outlen
           , "id=%lu, start=%s, last_update=%s\n\ttask_dones=%lu(%lu/s), errors=%lu(%lu/s), requests=%lu(%lu/s), responses=%lu(%lu/s)\n"
           , p_stat_node->i_thread_id
           , arr_start, arr_end
           , p_stat_node->st_thread_total.ul_task_dones, ul_task_dones_avg
           , p_stat_node->st_thread_total.ul_errors, ul_errors_avg
           , p_stat_node->st_thread_total.ul_requests, ul_requests_avg
           , p_stat_node->st_thread_total.ul_responses, ul_responses_avg);
    
    return;
}

/*******************************************************************************
*  FUNC     :  显示线程所有的信息
*  ARGS     :  pp_result            线程的所有信息
*  RTN      :  
*  NOTE     :  pp_result需要外面释放
*******************************************************************************/
void frstat_thread_output(char **pp_result)
{
    char *p_outbuff = NULL;
    char *p_result = NULL;
    int i_threads_all = 0;
    int i_out = 0;

    if(g_i_flag_init == FR_FAIL)
        return ;

    pthread_rwlock_rdlock(&g_stat_thread_rwlock);
    frhash2_show_all2(&g_st_hash_thread, &p_outbuff, __frstat_thread_output);
    i_threads_all = g_i_threads_total;
    pthread_rwlock_unlock(&g_stat_thread_rwlock);
    
    if(pp_result == NULL)
    {
        frdbg("==========================\n");
        printf("output stat\nthreads_total=%d\n%s\n", i_threads_all, p_outbuff);
        frdbg("==========================\n");
        frfree(p_outbuff);
        return;
    }
    i_out = (p_outbuff ? strlen(p_outbuff) : 0) + 128;
    
    p_result = frmalloc(i_out);
    if (p_result == NULL)
    {
        frfree(p_outbuff);
        return;
    }
    
    snprintf(p_result, i_out, "task threads=%d\n%s", i_threads_all, p_outbuff?p_outbuff:"");
    frfree(p_outbuff);
    *pp_result = p_result;
    
    return;
}

/*******************************************************************************
*  FUNC     :  初始化线程信息
*  ARGS     :  i_timeout        每次做平均的间隔时间
*  RTN      :  FR_SUCC   (成功)
*              FR_FAIL   (失败)
*  NOTE     :  调用bps,output,add都需要先运行过init (只运行一次)
*******************************************************************************/
int frstat_thread_init(int i_timeout)
{
    
    if (pthread_rwlock_init(&g_stat_thread_rwlock, NULL) != 0)
    {
        FRLOG_ERROR_DETAIL("pthread_rwlock_init failed");
        return FR_FAIL;
    }
    
    if (frhash2_create(&g_st_hash_thread, THREAD_STAT_HASH_SIZE) != FR_SUCC)
    {
        FRLOG_ERROR_DETAIL("frhash2_create failed");
        return FR_FAIL;
    }
    g_i_threads_total = 0;
    g_i_timeout = i_timeout;
    g_i_flag_init = FR_SUCC;
    
    return FR_SUCC;
}

/*******************************************************************************
*  FUNC     :  退出线程信息
*  ARGS     :  
*  RTN      :  
*  NOTE     :  调用完init后，使用完调用exit  (只能运行一次)
*******************************************************************************/
void frstat_thread_destory(void)
{
    if(g_i_flag_init == FR_FAIL)
        return;

    pthread_rwlock_wrlock(&g_stat_thread_rwlock);
    frhash2_free(&g_st_hash_thread, __thread_node_free);
    g_i_threads_total = 0;
    pthread_rwlock_unlock(&g_stat_thread_rwlock);
    
    pthread_rwlock_destroy(&g_stat_thread_rwlock);

    g_i_flag_init = FR_FAIL;
    return;
}
